package com.wujunshen.mongodb.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.Environment;
import org.springframework.validation.Validator;

import java.util.Objects;

/**
 * ConfigurationProperties 装载器，
 * 用于在bean definition阶段装载ConfigurationProperties
 */
@Slf4j
public class ConfigurationPropertiesLoader {
    private final Environment environment;
    private final ConfigurableListableBeanFactory beanFactory;

    public ConfigurationPropertiesLoader(Environment environment,
                                         ConfigurableListableBeanFactory beanFactory) {
        this.environment = environment;
        this.beanFactory = beanFactory;
    }

    /**
     * 装载configuration properties
     *
     * @param properties 配置类
     * @param <T>        返回继承Validator的配置类
     */
    public <T extends Validator> void load(T properties) {
        if (Objects.isNull(properties)) {
            log.error("\nconfiguration properties cannot be null\n");
            throw new IllegalArgumentException("configuration properties cannot be null");
        }

        final ConfigurationPropertiesBindingPostProcessor processor =
                new ConfigurationPropertiesBindingPostProcessor();
        processor.setEnvironment(environment);
        processor.setBeanFactory(beanFactory);
        processor.setConversionService(new DefaultConversionService());
        processor.setValidator(properties);

        registPropertySourcesPlaceholderConfigurer();

        PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer =
                beanFactory.getBean(PropertySourcesPlaceholderConfigurer.class);
        propertySourcesPlaceholderConfigurer.postProcessBeanFactory(beanFactory);

        try {
            processor.afterPropertiesSet();
        } catch (Exception e) {
            log.error("\nfailed to load configuration properties, error:{}\n",
                    ExceptionUtils.getStackTrace(e));
            throw new IllegalArgumentException(e);
        }
        processor.postProcessBeforeInitialization(properties, properties.getClass().getName());
    }

    /**
     * Registered PropertySourcesPlaceholderConfigurer in spring context when
     * PropertySourcesPlaceholderConfigurer not exists
     */
    private void registPropertySourcesPlaceholderConfigurer() {
        String[] propertySourcesPlaceholderConfigurerBeanNames = beanFactory
                .getBeanNamesForType(PropertySourcesPlaceholderConfigurer.class);
        if (ArrayUtils.isEmpty(propertySourcesPlaceholderConfigurerBeanNames)) {
            beanFactory.registerSingleton(PropertySourcesPlaceholderConfigurer.class.getSimpleName(),
                    new PropertySourcesPlaceholderConfigurer());
        }
    }
}